﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;

namespace Dycox.Drawing
{
    public abstract class DiagramLine : DiagramShape
    {
        protected DiagramLine(int minPoints)
            : this(minPoints, int.MaxValue)
        {
        }

        protected DiagramLine(int minPoints, int maxPoints)
        {
            if (minPoints < 2)
                throw new ArgumentOutOfRangeException("minPoints");
            if (maxPoints < minPoints)
                throw new ArgumentOutOfRangeException("maxPoints");

            _minPoints = minPoints;
            _maxPoints = maxPoints;

            _points = new List<PointF>(Math.Min(maxPoints, 10));
            SetPoint(_minPoints - 1, PointF.Empty);
        }

        int _minPoints, _maxPoints;

        List<PointF> _points;

        public PointF StartPoint
        {
            get { return GetPoint(0); }
            set { SetPoint(0, value); }
        }

        public PointF EndPoint
        {
            get { return GetPoint(PointCount - 1); }
            set { SetPoint(PointCount - 1, value); }
        }

        public int PointCount
        {
            get { return _points.Count; }
        }

        protected PointF GetPoint(int index)
        {
            if (index < PointCount)
                return _points[index];
            else
                return PointF.Empty;
        }

        protected void SetPoint(int index, PointF point)
        {
            SetPoint(index, point, true);
        }

        private void SetPoint(int index, PointF point, bool updateLayout)
        {
            if (index >= _maxPoints)
                throw new ArgumentOutOfRangeException();

            if (index >= _points.Count)
            {
                _points.AddRange(Enumerable.Repeat(PointF.Empty, index - _points.Count + 1));
            }

            _points[index] = point;

            if (index == 0 || index == _points.Count - 1)
            {
                if (updateLayout) SetHotPoints(StartPoint, EndPoint);
            }

            if (updateLayout)
            {
                GraphicsPath path = new GraphicsPath();
                path.AddLines(_points.ToArray());
                base.OutlinePath = path;
            }
        }

        protected void SetPoints(int startIndex, PointF[] points)
        {
            if (points == null)
                throw new ArgumentNullException("points");

            int length = points.Length;
            if (length > 0)
            {
                for (int i = length - 1; i >= 0; i--)
                {
                    SetPoint(startIndex + i, points[i], i == 0);
                }
            }
        }

        protected override void DrawShape(DrawShapeArgs args)
        {
            Track track = InitializeTrack();

            track.TextForeColor = this.TextColor;
            track.TrackColor = this.LineColor;
            track.Draw(args.Graphics);

            track.Dispose();
        }

        private Track InitializeTrack()
        {
            Track track = CreateTrack(_points.ToArray());
            track.TrackWidth = this.LineWidth;
            track.Text = this.Text;
            track.TextPosition = 0.5f;
            track.Font = this.Font;
            track.DashStyle = this.LineDashStyle;
            track.StartCap = _startCap;
            track.EndCap = _endCap;
            track.TrackColor = LineColor;

            return track;
        }

        private LineCap _startCap;

        public LineCap StartCap
        {
            get { return _startCap; }
            set { _startCap = value; }
        }

        private LineCap _endCap;

        public LineCap EndCap
        {
            get { return _endCap; }
            set { _endCap = value; }
        }


        protected virtual Track CreateTrack(PointF[] points)
        {
            Track track = Track.CreateLine(points);
            return track;
        }

        public override bool HitTest(Point location, Graphics g)
        {
            return InitializeTrack().HitTest(location, g);
        }
    }
}
